{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 装饰器的用法和用例\n",
    "\n",
    "- 参数检查装饰器\n",
    "- 缓存装饰器\n",
    "- 代理装饰器\n",
    "- 上下文装饰器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 参数检查（检查被装饰函数的输入输出参数类型）\n",
    "\n",
    "- XML-RPC：轻量级的远程过程调用协议，该协议提供了扩展，可以用来发现服务器的API，Python的xmlrpc模块实现了这个扩展。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "rpc_info = {}\n",
    "\n",
    "def xmlrpc(in_=(), out=(type(None), )):      # in_ , out 为输入输出的参数类型: in=(int, int), out=(None, type(None) , type(4)),etc\n",
    "    def _xmlrpc(function):\n",
    "        #注册签名\n",
    "        func_name = function.__name__\n",
    "        rpc_info[func_name] = (in_, out)\n",
    "        def _check_types(elements, types):\n",
    "            '''检查输入输出类型的子函数'''\n",
    "            if len(elements) != len(types):\n",
    "                raise TypeError('argument count is wrong')\n",
    "            typed = enumerate(zip(elements, types))\n",
    "            for index, couple in typed:\n",
    "                arg, type_ = couple\n",
    "                if isinstance(arg, type_):\n",
    "                    continue\n",
    "                raise TypeError('arg %d should be %s'%(index, type_))\n",
    "        # 包装过的函数\n",
    "        def __xmlrpc(*args): \n",
    "            # 检查输入的内容\n",
    "            checkable_args = args[1:]   # 去掉self\n",
    "            _check_types(checkable_args, in_)\n",
    "            # 调用被装饰的函数\n",
    "            result = function(*args)\n",
    "            # 检查输出的内容\n",
    "            if not type(result) in (tuple, list):\n",
    "                checkable_res = (result, )\n",
    "            else :\n",
    "                checkable_res = result\n",
    "            _check_types(checkable_res, out)\n",
    "            # 函数及其类型检查\n",
    "            return result\n",
    "        return __xmlrpc\n",
    "    return _xmlrpc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RPCView:\n",
    "    @xmlrpc((int, int))    # 装饰器将函数注册到全局字典中\n",
    "    def function_a(self, int1, int2):\n",
    "        print('received %d and %d'%(int1, int2))\n",
    "    \n",
    "    @xmlrpc((int, int), (float, float))\n",
    "    def function_b(self, int1, int2):\n",
    "        print('received %d and %d'%(int1, int2))\n",
    "        print('print return %f and %f'%(int1/2.0, int2/3.0))\n",
    "        return (int1/2., int2/3.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "\n",
    ">>>rpc_info\n",
    "{'function_a': ((int, int), (NoneType,)),\n",
    " 'function_b': ((int, int), (float, float))}\n",
    "\n",
    ">>>demo = RPCView()\n",
    ">>>demo.function_a(1, 2)\n",
    "received 1 and 2\n",
    "\n",
    ">>>demo.function_a(1.0,   2)\n",
    "TypeError                                 Traceback (most recent call last)\n",
    "----> 1 demo.function_a(1.0,   2)\n",
    "---> 22             _check_types(checkable_args, in_)\n",
    "---> 17                 raise TypeError('arg %d should be %s'%(index, type_))\n",
    "     TypeError: arg 0 should be <class 'int'>\n",
    "    \n",
    ">>>demo.function_b(1, 2)\n",
    "received 1 and 2\n",
    "print return 0.500000 and 0.666667\n",
    "(0.5, 0.6666666666666666)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 缓存\n",
    "\n",
    "- 与参数类型检查装饰器类似，但是重点关注内部状态不影响输出的函数\n",
    "- 可以把输出和计算它所需的参数放在一起，并在后续的调用中直接返回(memoizing)\n",
    "- 缓存计算代价较高的函数可以显著提高程序的性能，缓存也可以和函数本身绑定管理其作用域和周期\n",
    "- 推荐已经实现高级缓存算法的缓存库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "import pickle\n",
    "import hashlib\n",
    "\n",
    "cache = {}\n",
    "\n",
    "def is_obsolete(entry, duration):  # duration : 如果距离上次调用函数过了很长的时间，缓存会无效\n",
    "    return (time.time() - entry['time']) < duration\n",
    "\n",
    "def compute_key(function, args, kw):\n",
    "    key = pickle.dumps((function.__name__, args, kw))\n",
    "    return hashlib.sha1(key).hexdigest()  # 返回16进制的哈希值\n",
    "    \n",
    "def memoize(duration=10):\n",
    "    def _memoize(function):\n",
    "        def __memoize(*args, **kwargs):\n",
    "            key  = compute_key(function, args, kwargs)\n",
    "            # key是否在cache中\n",
    "            if (key in cache and  is_obsolete(cache[key], duration)):   # 据上次函数调用时间不超过duration,直接从cache中返回结果\n",
    "                print('This function is no more than 10s from the last call.')\n",
    "                print('So , we got the value in cache !!!')\n",
    "                return cache[key]['value']\n",
    "            else :\n",
    "                result = function(*args, **kwargs)\n",
    "                print('The cache has been refreshed and needs to be recalculated')\n",
    "                # 保存结果\n",
    "                cache[key] = {'value' : result, 'time':time.time()}\n",
    "            return result\n",
    "        return __memoize\n",
    "    return _memoize"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "metadata": {},
   "outputs": [],
   "source": [
    "@memoize(10)\n",
    "def a_very_complex_funcion(a, b):\n",
    "    # 计算时间长，计算机发热\n",
    "    # 可以考虑终止\n",
    "    return a+b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    ">>>a_very_complex_funcion(1, 2)\n",
    "The cache has been refreshed and needs to be recalculated\n",
    "\n",
    "3\n",
    ">>>a_very_complex_funcion(2, 3)\n",
    "The cache has been refreshed and needs to be recalculated\n",
    "\n",
    "5\n",
    ">>>a_very_complex_funcion(3, 4)\n",
    "The cache has been refreshed and needs to be recalculated\n",
    "\n",
    "7\n",
    ">>>cache\n",
    "{'2fc4d64416bfd19e3ee50db24c6705b8ed027c2e': {'value': 3,\n",
    "  'time': 1560933500.7079651},\n",
    " 'e169985e967d82eebb70cb1a3c96ebf2788e559a': {'value': 5,\n",
    "  'time': 1560933501.8255372},\n",
    " 'a6aaf959c612d819d4edfd3e91a306698708b670': {'value': 7,\n",
    "  'time': 1560933502.7970798}}\n",
    ">>>a_very_complex_funcion(3, 4)\n",
    "This function is no more than 10s from the last call.\n",
    "So , we got the value in cache !!!\n",
    "\n",
    "7\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 代理\n",
    "\n",
    "- 代理装饰器使用全局机制来标记和注册函数\n",
    "- 应用场景：web应用中，代理装饰器用来检查用户的角色和权限"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 189,
   "metadata": {},
   "outputs": [],
   "source": [
    "class User(object):\n",
    "    def __init__(self, roles):\n",
    "        self.roles = roles\n",
    "        \n",
    "class Unauthorized(Exception):\n",
    "    pass\n",
    "\n",
    "def protect(role):\n",
    "    def _protect(function):\n",
    "        def __protect(*args, **kwargs):\n",
    "            user = globals().get('user')   # globals()返回一个包含全局变量的字典，变量名的字符串为键值\n",
    "            if user is None or role not in user.roles:\n",
    "                raise Unauthorized('Permission denied')\n",
    "            return function(*args, **kwargs)\n",
    "        return __protect\n",
    "    return _protect"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 190,
   "metadata": {},
   "outputs": [],
   "source": [
    "tarek = User(('黄飞鸿','霍元甲','陈真','强森'))\n",
    "bill = User(('卖鱼强',))\n",
    "class MySecrets(object):\n",
    "    @protect('霍元甲')\n",
    "    def waffle_recipe(self):\n",
    "        print('you can do that')\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "you can do that\n"
     ]
    }
   ],
   "source": [
    "# 全局变量\n",
    "user = tarek\n",
    "secret = MySecrets()\n",
    "\n",
    "secret.waffle_recipe()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 197,
   "metadata": {},
   "outputs": [
    {
     "ename": "Unauthorized",
     "evalue": "Permission denied",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mUnauthorized\u001b[0m                              Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-197-11e90979498b>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0muser\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mUser\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'奥特曼'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'凹凸曼'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0msecret\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwaffle_recipe\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-189-229a93e73d79>\u001b[0m in \u001b[0;36m__protect\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m     11\u001b[0m             \u001b[0muser\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mglobals\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'user'\u001b[0m\u001b[0;34m)\u001b[0m   \u001b[0;31m# globals()返回一个包含全局变量的字典，变量名的字符串为键值\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     12\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0muser\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mrole\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0muser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroles\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m                 \u001b[0;32mraise\u001b[0m \u001b[0mUnauthorized\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Permission denied'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     14\u001b[0m             \u001b[0;32mreturn\u001b[0m \u001b[0mfunction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     15\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0m__protect\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mUnauthorized\u001b[0m: Permission denied"
     ]
    }
   ],
   "source": [
    "user = User(('奥特曼','凹凸曼'))\n",
    "secret.waffle_recipe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 上下文\n",
    "\n",
    "- 确保函数运行在正确的上下文中\n",
    "- 应用场景：比如一个数据项需要在多个线程之间共享，需要一个锁来保护他，避免多次访问，上下文装饰器可以实现这锁。\n",
    "- 上下文管理器with可以取代上下文装饰器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from threading import RLock\n",
    "\n",
    "lock = RLock()\n",
    "\n",
    "def synchronized(function):\n",
    "    def _synchronized(*args, **kwargs):\n",
    "        lock.acquire()\n",
    "        try :\n",
    "            return function(*args, **kwargs)\n",
    "        finally:\n",
    "            lock.release()\n",
    "        return _synchronized\n",
    "    \n",
    "@synchronized\n",
    "def thread_safe(): #确保锁定资源\n",
    "    pass"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
